﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Logic
{
    public class BpNetwork : Network
    {
        public double[] Err { get; set; }
        public void Learn(float[] expectedOutput, float gamma, float momentum)
        {
            var prevLayerOutputSignals = InputSignals;

            foreach (BpLayer baseLayer in Layers)
            {
                foreach (var neuron in baseLayer.Neurons)
                {
                    neuron.Delta = 0;
                }
                baseLayer.ProcessNeurons(prevLayerOutputSignals);
                prevLayerOutputSignals = baseLayer.GetOutputSignals();
            }

            var lastLayer = Layers.LastOrDefault() as BpLayer;
            if (LayersCount > 1)
            {
                prevLayerOutputSignals = Layers[LayersCount - 2].GetOutputSignals();
            }
            else
            {
                prevLayerOutputSignals = InputSignals;
            }
            for (int i = 0; i < lastLayer.Neurons.Length; i++)
            {
                Err[i] += Math.Pow(expectedOutput[i] - lastLayer.GetOutputSignals()[i], 2);
                var neuron = lastLayer.Neurons[i];
                neuron.Delta = (expectedOutput[i] - lastLayer.GetOutputSignals()[i]) * neuron.ComputeWithDerrivative(lastLayer.ActivationFunction, prevLayerOutputSignals);
            }

            for (int i = LayersCount - 1; i >= 1; i--)
            {
                var layer = Layers[i] as BpLayer;
                var prevLayer = Layers[i - 1] as BpLayer;
                foreach (var layerNeuron in layer.Neurons)
                {
                    for (int j = 0; j < layer.Neurons[0].Weights.Length; j++)
                    {
                        var layerNeuronWeight = layerNeuron.Weights[j];
                        prevLayer.Neurons[j].Delta += layerNeuron.Delta*layerNeuronWeight;                        
                    }
                }

                if (i > 1)
                {
                    var praprevLayer = Layers[i - 2] as BpLayer;
                    foreach (var neuron in prevLayer.Neurons)
                    {
                        neuron.Delta *= neuron.ComputeWithDerrivative(prevLayer.ActivationFunction, praprevLayer.GetOutputSignals());
                    }
                }
                else if (i==1)
                {
                    foreach (var neuron in prevLayer.Neurons)
                    {
                        neuron.Delta *= neuron.ComputeWithDerrivative(prevLayer.ActivationFunction, InputSignals);
                    }
                }

            }

            //aktualizacja wag
            var prevLayerSignals = InputSignals;

            foreach (BpLayer baseLayer in Layers)
            {
                var tmp = baseLayer.GetOutputSignals();
                foreach (var neuron in baseLayer.Neurons)
                {
                    int j = 0;
                    foreach (var weight in neuron.Weights)
                    {
                        var newWeight = weight +
                                        gamma*neuron.Delta*
                                        prevLayerSignals[j] + momentum*(weight-neuron.OldWeights[j]);
                        neuron.Weights[j] = newWeight;
                        neuron.OldWeights[j] = weight;

                        j++;
                    }
                }
                prevLayerSignals = tmp;
            }


        }

        public static BpNetwork LoadNetwork(String path)
        {
            var network = new BpNetwork();
            String[] lines = File.ReadLines(path).ToArray();
            int layersCount = int.Parse(lines[0]);
            network.Layers = new BaseLayer[layersCount];
            int inputsCount = int.Parse(lines[1]);
            network.InputLayerSize = inputsCount;
            int layerId = 0;
            int lineNo = 2;
            int prevLayerSize = inputsCount;
            while (layerId < layersCount)
            {
                String type = lines[lineNo++];
                int neuronsCount;
                BaseLayer currentLayer;
                String[] tmp = lines[lineNo++].Split();
                if (type.Equals("BpLayer"))
                {
                    var layer = new BpLayer();
                    currentLayer = layer;
                    neuronsCount = int.Parse(tmp[0]);
                    layer.Neurons = new Neuron[neuronsCount];
                    layer.ActivationFunction = ResourceManager.GetActivationFunctionBySymbol(tmp[1]);
                    for (int neuronId = 0; neuronId < neuronsCount; neuronId++)
                    {
                        var neuron = new Neuron(prevLayerSize);
                        neuron.Bias = float.Parse(lines[lineNo++]);
                        layer.Neurons[neuronId] = neuron;
                    }
                    layer.InitializeWeights(-1, 1); //inicjalizacja losowymi wartościami


                    network.Layers[layerId++] = currentLayer;
                    prevLayerSize = currentLayer.Size;
                }
            }
            network.Err = new double[network.Layers.LastOrDefault().Size];
            return network;
        }
    }
}
